﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Threading;

namespace Lib
{
    public class Apriori
    {
        public TransactionData Data;
        public double minsup;
        public double minconf;
        FrequentItem freq = new FrequentItem();
        public Dictionary<List<TransactionValue>, double> fiList;



        public List<FrequentItem> FI = new List<FrequentItem>();


        public long startTime;
        public long endTime;


        public Apriori(TransactionData data, double supp)
        {
            Data = data;
            minsup = supp;
            fiList = new Dictionary<List<TransactionValue>, double>();
        }

        public Apriori() { }
        /// <summary>
        /// Tính độ hỗ trợ
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private int Support(List<TransactionValue> s) 
        {
            int support = 0;
            int count = 0;
            foreach (var trans in Data.Transactions)
            {
                if (CheckSubset(s, trans))
                    count++;
            }
            support = (count);
            return support;
        }

        private bool CheckSubset(List<TransactionValue> child, Transaction parent) // Ham kiem tra tap con
        {
            foreach (var item in child)
            {
                if (parent.Value.Find(x => x.Name == item.Name && x.AttIndex == item.AttIndex) == null)
                    return false;
            }
            return true;
        }

        public Dictionary<List<TransactionValue>, double> GetL1()
        {
            var L1 = new Dictionary<List<TransactionValue>, double>();
            List<TransactionValue> items = GetItem();
            foreach (var item in items)
            {
                List<TransactionValue> key = new List<TransactionValue>();
                if (item.Count >= minsup)
                {

                    key.Add(new TransactionValue(item.Name, item.AttIndex,item.AttName));
                    fiList.Add(key, item.Count); 
                    FI.Add(new FrequentItem { Name = key, Supp = item.Count }); // test
                    L1.Add(key, item.Count);
                }
            }
            return L1;
        }

        /// <summary>
        /// Lấy giá trị của mỗi thuộc tính
        /// </summary>
        /// <returns></returns>
        public List<TransactionValue> GetItem()
        {
            List<TransactionValue> uniqueItems = new List<TransactionValue>();
            foreach (var attvalue in Data.Attributes)
            {
                uniqueItems.AddRange((from x in attvalue.AttValue
                                      select x));
            }
            return uniqueItems;
        }
        /// <summary>
        /// Hàm sinh tập ứng viên
        /// </summary>
        /// <param name="frequentItem"></param>
        /// <returns></returns>
        public Dictionary<List<TransactionValue>, double> Apriori_Gen(Dictionary<List<TransactionValue>, double> frequentItem)
        {
            Dictionary<List<TransactionValue>, double> candidates = new Dictionary<List<TransactionValue>, double>();
            List<TransactionValue> candidateValue;// = new List<TransactionValue>();
            int candidateSupport = 0;
            for (int i = 0; i < frequentItem.Count - 1; i++)
            {
                List<TransactionValue> first = Sort(frequentItem.Keys.ToList()[i]);

                for (int j = i + 1; j < frequentItem.Count; j++)
                {
                    candidateValue = new List<TransactionValue>();
                    List<TransactionValue> firsttam = new List<TransactionValue>(first);
                    List<TransactionValue> second = Sort(frequentItem.Keys.ToList()[j]);
                    candidateValue = Join(firsttam, second);
                    if (candidateValue != null)
                    {
                        candidateValue = Sort(candidateValue);
                        candidateSupport = Support(candidateValue);
                        if (Has_infrequent_Subset(candidateValue, frequentItem) && candidateValue.Count > 2)
                        {
                            candidateValue = new List<TransactionValue>();
                        }
                        else
                        {
                            candidates.Add(candidateValue, candidateSupport);
                        }
                    }
                }
            }
            return candidates;
        }

        private List<TransactionValue> Sort(List<TransactionValue> item) // Ham sap xep theo thu tu
        {
            item.OrderBy(x => x.AttIndex);
            List<TransactionValue> result = new List<TransactionValue>(item);
            return result;
        }
        /// <summary>
        /// Tạo ứng viên
        /// </summary>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <returns></returns>
        private List<TransactionValue> Join(List<TransactionValue> first, List<TransactionValue> second)
        {
            int lenght = first.Count;
            List<TransactionValue> result = new List<TransactionValue>();
            if (lenght == 1)
            {
                foreach (var item in second)
                {
                    first.Add(item);
                }
                return first;
            }
            else
            {
                List<TransactionValue> firstSub = new List<TransactionValue>();
                List<TransactionValue> secondSub = new List<TransactionValue>();
                for (int i = 0; i < first.Count - 1; i++)
                {
                    firstSub.Add(first[i]);
                }
                for (int j = 0; j < second.Count - 1; j++)
                {
                    secondSub.Add(second[j]);
                }

                List<TransactionValue> result2 = firstSub.Except(secondSub).ToList();
                if (result2.Count == 0)
                {
                    TransactionValue last = second.Last();
                    first.Add(last);
                    return first;
                }
            }
            return null;
        }
        /// <summary>
        /// Hàm kiếm tra các tập con của mỗi ứng viên có trong tập Phổ biến trước hay không
        /// </summary>
        /// <param name="candidateValue"></param>
        /// <param name="frequentItem"></param>
        /// <returns></returns>
        private bool Has_infrequent_Subset(List<TransactionValue> candidateValue, Dictionary<List<TransactionValue>, double> frequentItem)
        {
            List<List<TransactionValue>> subSetList = SubSetCandidate(candidateValue);
            foreach (var item in subSetList)
            {
                if (frequentItem.Keys.Select(x => x = item) == null)
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Hàm tạo tập con của tập ứng viên.
        /// </summary>
        /// <param name="set"></param>
        /// <returns></returns>
        private List<List<TransactionValue>> SubSetCandidate(List<TransactionValue> set)
        {
            int lenght = set.Count;
            List<List<TransactionValue>> list = new List<List<TransactionValue>>();
            foreach (var item in set)
            {
                List<TransactionValue> tam = new List<TransactionValue>(set);
                tam.Remove(item);
                list.Add(tam);
            }
            return list;
        }

        /// <summary>
        /// Tạo tập phổ biến Lk+1 dựa vào tập ứng viên Ck.
        /// </summary>
        /// <param name="candidate"></param>
        /// <returns></returns>
        private Dictionary<List<TransactionValue>, double> GenerateFrequent(Dictionary<List<TransactionValue>, double> candidate)
        {
            var frequent = new Dictionary<List<TransactionValue>, double>();
            foreach (var item in candidate)
            {
                if (item.Value >= minsup)
                {
                    frequent.Add(item.Key, item.Value);
                    fiList.Add(item.Key.OrderBy(x=>x.AttIndex).ToList(), item.Value); 
                    FI.Add(new FrequentItem { Name = item.Key.OrderBy(x => x.AttIndex).ToList() , Supp = item.Value});// test
                }
            }
            return frequent;
        }

        public void RunAlgoApriori()
        {            
            startTime = DateTime.Now.TimeOfDay.Milliseconds;
            Dictionary<List<TransactionValue>, double> frequentitemset = GetL1();
            Dictionary<List<TransactionValue>, double> candidates = new Dictionary<List<TransactionValue>, double>();
            do
            {
                candidates = Apriori_Gen(frequentitemset);
                frequentitemset = GenerateFrequent(candidates);
            }
            while (frequentitemset.Count != 0);
            endTime = DateTime.Now.TimeOfDay.Milliseconds;
        }

    }
}
